#include <math.h>

#include <cstdlib> 
#include <iostream>

#include "Sensors.h"

//  Equations taken from http://www.srrb.noaa.gov/highlights/sunrise/solareqns.PDF

namespace Pennyworth
{
	namespace Sensors
	{
		double fractionalYear()
		{
			time_t time_now = time(NULL);
			struct tm * now = (tm *) malloc(sizeof(tm)); 
			localtime_r(&time_now, now);

			double day_of_year = (double) now->tm_yday;	
			double hour_of_day = (double) now->tm_hour;	

			free(now);
			
			return ((2 * M_PI) / 365) * (day_of_year + ((hour_of_day - 12) / 24));
		}

		double eqTime()
		{
			double fractYear = fractionalYear();
	
			double et = 229.18 * (0.000075 + (0.001868 * cos (fractYear)) - (0.032077 * sin (fractYear)) - 
						   		 (0.014615 * cos (2 * fractYear)) - (0.040849 * sin (2 * fractYear)));
	
			return et;
		}

		double solarDeclination()
		{
			double fractYear = fractionalYear();
	
			double decl = 0.006918 - (0.399912 * cos (fractYear)) + (0.070257 * sin (fractYear)) - (0.006758 * cos (2 * fractYear)) +
		   							 (0.000907 * sin (2 * fractYear)) - (0.002697 * cos (3 * fractYear)) + (0.00148 * sin (3 * fractYear));
	
			return decl;
		}

		double radiansForDegrees(double degrees)
		{
			return (M_PI / 180.0) * degrees;
		}

		double degreesForRadians(double rads)
		{
			return (180 / M_PI) * rads;
		}

		double hourAngle(double latitude, double longitude)
		{
			double decl = solarDeclination();

			double ha = acos ((cos(radiansForDegrees(90.833)) / (cos(radiansForDegrees(latitude)) * cos(decl))) - 
							  (tan(radiansForDegrees(latitude)) * tan(decl)));
	
			return ha;
		}

		double sunriseForLocation(double latitude, double longitude)
		{
			return 720 + (4 * (longitude - degreesForRadians(hourAngle(latitude, longitude)))) - eqTime();
		}

		double sunsetForLocation(double latitude, double longitude)
		{
			return 720 + (4 * (longitude + degreesForRadians(hourAngle(latitude, longitude)))) - eqTime();
		}

		double solarNoonForLocation(double latitude, double longitude)
		{
			return 720 + (4 * longitude) - eqTime();
		}

		double DaylightSensor::sunrise()
		{
			time_t time_now = time(NULL);
			struct tm * now = (tm *) malloc(sizeof(tm)); 
			localtime_r(&time_now, now);
			
			double gmc_offset = (double) now->tm_gmtoff;
			
			free(now);
		
			double utcSunrise = sunriseForLocation(this->latitude(), this->longitude()) + (gmc_offset / 60);
		}

		double DaylightSensor::sunset()
		{
			time_t time_now = time(NULL);
			struct tm * now = (tm *) malloc(sizeof(tm)); 
			localtime_r(&time_now, now);
			
			double gmc_offset = (double) now->tm_gmtoff;
			
			free(now);
		
			double utcSunrise = sunsetForLocation(this->latitude(), this->longitude()) + (gmc_offset / 60);
		}

		DaylightSensor::DaylightSensor() 
			: Sensor::Sensor()
		{
			delete this->sensor_name;
			delete this->sensor_description;
			
			this->sensor_name = new std::wstring(L"GMT Daylight Sensor");
			this->sensor_description = new std::wstring(L"Daylight sensor for the location (0, 0).");

			this->_latitude = 0;
			this->_longitude = 0;
		}

		DaylightSensor::DaylightSensor (std::wstring * name, std::wstring * description, int identifier, std::map<std::wstring, std::wstring> * options) 
			: Sensor::Sensor(name, description, identifier, options)
			
		{
			this->_latitude = 0;
			this->_longitude = 0;
		}
		
		double DaylightSensor::latitude()
		{
			return this->_latitude;
		}

		void DaylightSensor::setLatitude(double latitude)
		{
			this->_latitude = latitude;
		}

		double DaylightSensor::longitude()
		{
			return this->_longitude;
		}

		void DaylightSensor::setLongitude(double longitude)
		{
			this->_longitude = longitude;
		}
	}
}
